package ua.lviv.franko.solvers.oneDimention;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ua.lviv.franko.integration.ConstantFunction1D;
import ua.lviv.franko.integration.GaussQuadrature;
import ua.lviv.franko.integration.IFunction1D;
import ua.lviv.franko.integration.Quadrature;
import ua.lviv.franko.matrix.RowType;
import ua.lviv.franko.matrix.hash.lu.HashMatrixLUStyle;
import ua.lviv.franko.matrix.hash.lu.LUFactorization;
import ua.lviv.franko.mlsFunctions.oneDimension.BaseFunction;
import ua.lviv.franko.mlsFunctions.oneDimension.Particle;
import ua.lviv.franko.mlsFunctions.oneDimension.weightFunctions.CubicSpline;
import ua.lviv.franko.mlsFunctions.oneDimension.weightFunctions.IWeightFunction;

public class DiffusionSimpleMFSolver {

	// general data
	protected static final int		QUADRATURE_POINTS_NUMBER	= 7;
	protected Quadrature			gau;
	protected EquationParameters	parameters;
	protected HashMatrixLUStyle		mainMatrix;
	protected List<Double>			bTmpVector;
	protected double[]				b;
	protected double				leftBoundery;
	protected double				rightBoundery;
	protected BoundaryCondition		leftBC;
	protected BoundaryCondition		rightBC;

	// problem data
	protected double				kKk1;
	protected double				kKk2;
	protected double				kKk3;
	protected IFunction1D			rho1;
	protected IFunction1D			rho3;
	protected IFunction1D			mu1;

	// data for base functions
	protected List<Particle>		particles;
	protected List<BaseFunction>	baseFunctions;

	public DiffusionSimpleMFSolver(final EquationParameters parameters, List<Particle> particles, double leftBoundery, double rightBoundery, int PolinomialDegree,
			IWeightFunction weight) {
		super();
		this.parameters = parameters;
		this.particles = particles;
		this.rightBoundery = rightBoundery;
		this.leftBoundery = leftBoundery;

		gau = new GaussQuadrature(QUADRATURE_POINTS_NUMBER);
		// gau = new TrapezoidalRuleQuadrature(QUADRATURE_POINTS_NUMBER);

		// creation of base functions;
		this.createBaseFunctions(PolinomialDegree, weight);
	}

	public void generateMatrix() {
		mainMatrix = new HashMatrixLUStyle(RowType.ARRAY_TYPE, (particles.size()));
		// b = new double[(particles.size()) ];
		bTmpVector = new ArrayList<Double>((particles.size()));
		for (int i = 0; i < mainMatrix.getN(); i++) {
			bTmpVector.add(0.0);
		}
		for (int i = 0; i < baseFunctions.size(); i++) {
			final BaseFunction bfi = baseFunctions.get(i);
			for (int j = 0; j < baseFunctions.size(); j++) {
				final BaseFunction bfj = baseFunctions.get(j);
				IFunction1D tmpFunc = new IFunction1D() {

					@Override
					public double calculate(double x) {
						double fiVal = bfi.calculate(x);
						double fiDer = bfi.calculateDerivaty(x);
						double fjVal = bfj.calculate(x);
						double fjDer = bfj.calculateDerivaty(x);
						double p = parameters.p.calculate(x);
						double w = parameters.w.calculate(x);
						double q = parameters.q.calculate(x);
						return fiDer * fjDer * p + fjDer * fiVal * w + fiVal * fjVal * q;
					}

					@Override
					public double calculateDerivaty(double x) {

						return 0.0;
					}

				};

				double tmp = gau.integrate(Math.max(Math.max(bfi.getXi().getXi() - bfi.getXi().getRho(), bfj.getXi().getXi() - bfj.getXi().getRho()), leftBoundery), bfi.getXi()
						.getXi(), tmpFunc);

				double tmp1 = gau.integrate(bfi.getXi().getXi(),
						Math.min(Math.min(bfi.getXi().getXi() + bfi.getXi().getRho(), bfj.getXi().getXi() + bfj.getXi().getRho()), rightBoundery), tmpFunc);

				mainMatrix.setValue(i, j, tmp + tmp1);
			}

			// right hand side
			double vector = gau.integrate(Math.max(bfi.getXi().getXi() - bfi.getXi().getRho(), leftBoundery), Math.min(bfi.getXi().getXi() + bfi.getXi().getRho(), rightBoundery),
					new IFunction1D() {

						@Override
						public double calculate(double x) {
							double fiVal = bfi.calculate(x);

							return fiVal * parameters.f.calculate(x);
						}

						@Override
						public double calculateDerivaty(double x) {

							return 0.0;
						}

					});
			bTmpVector.set(i, vector);
		}

	}

	public BoundaryCondition getLeftBC() {
		return leftBC;
	}

	public BoundaryCondition getRightBC() {
		return rightBC;
	}

	public void setLeftBC(BoundaryCondition leftBC) {
		this.leftBC = leftBC;
	}

	public void setRightBC(BoundaryCondition rightBC) {
		this.rightBC = rightBC;
	}

	public EFGDiffusionResult1D solve() {
		if (leftBC.isDirihlet()) {
			mainMatrix.addRows(1);
			BaseFunction bf = baseFunctions.get(0);
			double[] vals = bf.calculateAllFunctionsValuesOnly(leftBoundery);
			int size = mainMatrix.getN();
			for (int i = 0; i < vals.length; i++) {
				mainMatrix.setValue(size - 1, i, vals[i]);
				mainMatrix.setValue(i, size - 1, vals[i]);
			}

			bTmpVector.add(leftBC.value);
		}
		if (rightBC.isDirihlet()) {
			mainMatrix.addRows(1);
			BaseFunction bf = baseFunctions.get(baseFunctions.size() - 1);
			double[] vals = bf.calculateAllFunctionsValuesOnly(rightBoundery);
			int size = mainMatrix.getN();
			for (int i = 0; i < vals.length; i++) {
				mainMatrix.setValue(size - 1, i, vals[i]);
				mainMatrix.setValue(i, size - 1, vals[i]);
			}
			bTmpVector.add(rightBC.value);
		}
		b = new double[bTmpVector.size()];
		for (int i = 0; i < b.length; i++) {
			b[i] = bTmpVector.get(i);
		}

		HashMatrixLUStyle.printMatrix(mainMatrix);
		System.out.println();
		System.out.println();
		HashMatrixLUStyle.printMatrix(b);
		LUFactorization lu = new LUFactorization(this.mainMatrix);
		double[] results = lu.calculate(b);

		// for (int i = 0; i < particles.size(); i++) {
		// System.out.println(results[i*NUMBER_OF_UNKNOWNS]);
		// }
		return new EFGDiffusionResult1D(results, baseFunctions, particles);
	}

	private void createBaseFunctions(int polinomialDegree, IWeightFunction weight) {
		baseFunctions = new ArrayList<BaseFunction>();
		for (Iterator<Particle> iterator = particles.iterator(); iterator.hasNext();) {
			Particle particle = iterator.next();
			BaseFunction bf = new BaseFunction(polinomialDegree, particle, weight);
			bf.setNodeList(this.getParticleSupport(particle, weight));
			this.baseFunctions.add(bf);
		}

	}

	private List<Particle> getParticleSupport(Particle particle, IWeightFunction weight) {
		List<Particle> list = new ArrayList<Particle>();
		for (Iterator<Particle> iterator = particles.iterator(); iterator.hasNext();) {
			Particle p = (Particle) iterator.next();
			if (weight.intersectedSupport(particle, p)) {
				list.add(p);
			}
		}
		// return list;
		return particles;
	}

	public static void main(String[] args) {
		EquationParameters eqP = new EquationParameters(new ConstantFunction1D(1.0), new ConstantFunction1D(100.0), new ConstantFunction1D(0.0), new ConstantFunction1D(100.0));
		double a = 0.0;
		double b = 1.0;
		int n = 10;
		double hStep = (b - a) / (double) n;
		double rho = 1.001 * hStep;
		List<Particle> particles = new ArrayList<Particle>();

		for (int i = 0; i <= n; i++) {
			particles.add(new Particle(a + i * hStep, rho));
		}

		int PolinomialDegree = 1;
		IWeightFunction weight = new CubicSpline();
		DiffusionSimpleMFSolver solver = new DiffusionSimpleMFSolver(eqP, particles, a, b, PolinomialDegree, weight);
		solver.setLeftBC(new BoundaryCondition(0, true));
		solver.setRightBC(new BoundaryCondition(0, true));
		long start = System.nanoTime();
		solver.generateMatrix();
		EFGDiffusionResult1D diffusionResult1D = solver.solve();
		System.out.println("DiffusionSimpleMFSolver - " + (System.nanoTime() - start));
		diffusionResult1D.display(a, b);

	}

}
